/*
 * Copyright (c) 2021 Zsj. All rights reserved.
 */

package com.jzz.crawlerweb.handler.config;

import com.jzz.crawlerweb.handler.handler.AbstractCaptchaHandler;
import com.jzz.crawlerweb.handler.props.CaptchaProps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.UUID;

public class CaptchaWebInterceptor implements HandlerInterceptor {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    private AbstractCaptchaHandler<?> captchaHandler;
    private final CaptchaProps.Case theCase;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    private final CaptchaProps captchaProps;

    public CaptchaWebInterceptor(CaptchaProps.Case theCase, CaptchaProps captchaProps) {
        this.theCase = theCase;
        this.captchaProps = captchaProps;
        initCaptchaHandler();
    }

    public void initCaptchaHandler() {
        captchaHandler = AbstractCaptchaHandler.getInstance(theCase.getType());
        logger.info("Captcha type: {}, paths: [{}]", theCase.getType(), Arrays.toString(theCase.getPaths()));
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (captchaHandler == null) {
            return true;
        }

        logger.info("PreHandle request using [{}]", captchaHandler);

        HttpSession session = request.getSession();
        logger.info("Get session id [{}]", session.getId());

        // 判断该Session是否通过了验证
        Object success = session.getAttribute("success");
        if ("true".equals(success)) {
            logger.info("Session [{}] login success", session.getId());
            return true;
        }

        if (request.getParameter(captchaProps.getParamName()) != null
                && session.getAttribute(captchaProps.getParamName() + ":" + theCase.getType()) != null) {
            // 用户传递了验证码，进行验证
            if (captchaHandler.verify(
                    request.getParameter(captchaProps.getParamName()),
                    session.getAttribute(captchaProps.getParamName() + ":" + theCase.getType()).toString())) {
                session.setAttribute("success", "true");
                session.setAttribute("secret", UUID.randomUUID().toString());
            } else {
                // 直接返回验证码错误，不进行接下来的流程
                response.setStatus(200);
                response.setContentType("application/json; charset=utf-8");
                PrintWriter writer = response.getWriter();
                writer.write("{\"code\": -1, \"message\": \"验证码错误\"}");
                writer.flush();
                writer.close();
                return false;
            }
        } else {
            /*
             * 用户未传递验证码，或目前未有验证码,有以下情况：
             * 1.前端代码缺陷，或恶意请求，应予以阻止
             * 2.访问了合法的获取验证码的接口，应予以通过
             * 3.访问了合法的其他资源的接口，应予以通过
             * */

            // 检验是否访问了开放资源
            for (String pattern : theCase.getOpenPaths()) {
                if (antPathMatcher.match(pattern, request.getServletPath())) {
                    // 生成验证码并保存
                    session.setAttribute(captchaProps.getParamName() + ":" + theCase.getType(),
                            captchaHandler.generate().getRaw());
                    session.setAttribute("captcha-type", theCase.getType());
                    return true;
                }
            }

            // 检验是否访问了保护资源
            for (String pattern : theCase.getProtectPaths()) {
                if (antPathMatcher.match(pattern, request.getServletPath())) {
                    response.setStatus(401);
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    writer.write("{\"code\": -1, \"message\": \"非法访问保护资源\"}");
                    writer.flush();
                    writer.close();
                    logger.warn("Illegal request: [{}] from session id [{}]", request.getServletPath(), session.getId());
                    return false;
                }
            }
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
